#pragma once

#include <uacpi/types.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum uacpi_resource_type {
    UACPI_RESOURCE_TYPE_IRQ,
    UACPI_RESOURCE_TYPE_EXTENDED_IRQ,

    UACPI_RESOURCE_TYPE_DMA,
    UACPI_RESOURCE_TYPE_FIXED_DMA,

    UACPI_RESOURCE_TYPE_IO,
    UACPI_RESOURCE_TYPE_FIXED_IO,

    UACPI_RESOURCE_TYPE_ADDRESS16,
    UACPI_RESOURCE_TYPE_ADDRESS32,
    UACPI_RESOURCE_TYPE_ADDRESS64,
    UACPI_RESOURCE_TYPE_ADDRESS64_EXTENDED,

    UACPI_RESOURCE_TYPE_MEMORY24,
    UACPI_RESOURCE_TYPE_MEMORY32,
    UACPI_RESOURCE_TYPE_FIXED_MEMORY32,

    UACPI_RESOURCE_TYPE_START_DEPENDENT,
    UACPI_RESOURCE_TYPE_END_DEPENDENT,

    // Up to 7 bytes
    UACPI_RESOURCE_TYPE_VENDOR_SMALL,

    // Up to 2^16 - 1 bytes
    UACPI_RESOURCE_TYPE_VENDOR_LARGE,

    UACPI_RESOURCE_TYPE_GENERIC_REGISTER,
    UACPI_RESOURCE_TYPE_GPIO_CONNECTION,

    // These must always be contiguous in this order
    UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION,
    UACPI_RESOURCE_TYPE_SERIAL_SPI_CONNECTION,
    UACPI_RESOURCE_TYPE_SERIAL_UART_CONNECTION,
    UACPI_RESOURCE_TYPE_SERIAL_CSI2_CONNECTION,

    UACPI_RESOURCE_TYPE_PIN_FUNCTION,
    UACPI_RESOURCE_TYPE_PIN_CONFIGURATION,
    UACPI_RESOURCE_TYPE_PIN_GROUP,
    UACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION,
    UACPI_RESOURCE_TYPE_PIN_GROUP_CONFIGURATION,

    UACPI_RESOURCE_TYPE_CLOCK_INPUT,

    UACPI_RESOURCE_TYPE_END_TAG,
    UACPI_RESOURCE_TYPE_MAX = UACPI_RESOURCE_TYPE_END_TAG,
} uacpi_resource_type;

typedef struct uacpi_resource_source {
    uacpi_u8 index;
    uacpi_bool index_present;
    uacpi_u16 length;
    uacpi_char *string;
} uacpi_resource_source;

/*
 * This applies to IRQ & StartDependent resources only. The DONT_CARE value is
 * used for deserialization into the AML format to signify that the serializer
 * is allowed to optimize the length down if possible. Note that this is
 * generally not allowed unless the resource is generated by the caller:
 *
 * -- ACPI 6.5 ------------------------------------------------------------
 * The resource descriptors in the byte stream argument must be specified
 * exactly as listed in the _CRS byte stream - meaning that the identical
 * resource descriptors must appear in the identical order, resulting in a
 * buffer of exactly the same length. Optimizations such as changing an
 * IRQ descriptor to an IRQNoFlags descriptor (or vice-versa) must not be
 * performed. Similarly, changing StartDependentFn to StartDependentFnNoPri
 * is not allowed.
 * ------------------------------------------------------------------------
 */
enum uacpi_resource_length_kind {
    UACPI_RESOURCE_LENGTH_KIND_DONT_CARE = 0,
    UACPI_RESOURCE_LENGTH_KIND_ONE_LESS,
    UACPI_RESOURCE_LENGTH_KIND_FULL,
};

// triggering fields
#define UACPI_TRIGGERING_EDGE 1
#define UACPI_TRIGGERING_LEVEL 0

// polarity
#define UACPI_POLARITY_ACTIVE_HIGH 0
#define UACPI_POLARITY_ACTIVE_LOW 1
#define UACPI_POLARITY_ACTIVE_BOTH 2

// sharing
#define UACPI_EXCLUSIVE 0
#define UACPI_SHARED 1

// wake_capability
#define UACPI_WAKE_CAPABLE 1
#define UACPI_NOT_WAKE_CAPABLE 0

typedef struct uacpi_resource_irq {
    uacpi_u8 length_kind;
    uacpi_u8 triggering;
    uacpi_u8 polarity;
    uacpi_u8 sharing;
    uacpi_u8 wake_capability;
    uacpi_u8 num_irqs;
    uacpi_u8 irqs[];
} uacpi_resource_irq;

typedef struct uacpi_resource_extended_irq {
    uacpi_u8 direction;
    uacpi_u8 triggering;
    uacpi_u8 polarity;
    uacpi_u8 sharing;
    uacpi_u8 wake_capability;
    uacpi_u8 num_irqs;
    uacpi_resource_source source;
    uacpi_u32 irqs[];
} uacpi_resource_extended_irq;

// transfer_type
#define UACPI_TRANSFER_TYPE_8_BIT 0b00
#define UACPI_TRANSFER_TYPE_8_AND_16_BIT 0b01
#define UACPI_TRANSFER_TYPE_16_BIT 0b10

// bus_master_status
#define UACPI_BUS_MASTER 0b1

// channel_speed
#define UACPI_DMA_COMPATIBILITY 0b00
#define UACPI_DMA_TYPE_A 0b01
#define UACPI_DMA_TYPE_B 0b10
#define UACPI_DMA_TYPE_F 0b11

// transfer_width
#define UACPI_TRANSFER_WIDTH_8 0x00
#define UACPI_TRANSFER_WIDTH_16 0x01
#define UACPI_TRANSFER_WIDTH_32 0x02
#define UACPI_TRANSFER_WIDTH_64 0x03
#define UACPI_TRANSFER_WIDTH_128 0x04
#define UACPI_TRANSFER_WIDTH_256 0x05

typedef struct uacpi_resource_dma {
    uacpi_u8 transfer_type;
    uacpi_u8 bus_master_status;
    uacpi_u8 channel_speed;
    uacpi_u8 num_channels;
    uacpi_u8 channels[];
} uacpi_resource_dma;

typedef struct uacpi_resource_fixed_dma {
    uacpi_u16 request_line;
    uacpi_u16 channel;
    uacpi_u8 transfer_width;
} uacpi_resource_fixed_dma;

// decode_type
#define UACPI_DECODE_16 0b1
#define UACPI_DECODE_10 0b0

typedef struct uacpi_resource_io {
    uacpi_u8 decode_type;
    uacpi_u16 minimum;
    uacpi_u16 maximum;
    uacpi_u8 alignment;
    uacpi_u8 length;
} uacpi_resource_io;

typedef struct uacpi_resource_fixed_io {
    uacpi_u16 address;
    uacpi_u8 length;
} uacpi_resource_fixed_io;

// write_status
#define UACPI_NON_WRITABLE 0
#define UACPI_WRITABLE 1

// caching
#define UACPI_NON_CACHEABLE 0
#define UACPI_CACHEABLE 1
#define UACPI_CACHEABLE_WRITE_COMBINING 2
#define UACPI_PREFETCHABLE 3

// range_type
#define UACPI_RANGE_TYPE_MEMORY 0
#define UACPI_RANGE_TYPE_RESERVED 1
#define UACPI_RANGE_TYPE_ACPI 2
#define UACPI_RANGE_TYPE_NVS 3

// address_common->type
#define UACPI_RANGE_MEMORY 0
#define UACPI_RANGE_IO 1
#define UACPI_RANGE_BUS 2

// translation
#define UACPI_IO_MEM_TRANSLATION 1
#define UACPI_IO_MEM_STATIC 0

// translation_type
#define UACPI_TRANSLATION_DENSE 0
#define UACPI_TRANSLATION_SPARSE 1

// direction
#define UACPI_PRODUCER 0
#define UACPI_CONSUMER 1

// decode_type
#define UACPI_POISITIVE_DECODE 0
#define UACPI_SUBTRACTIVE_DECODE 1

// fixed_min_address & fixed_max_address
#define UACPI_ADDRESS_NOT_FIXED 0
#define UACPI_ADDRESS_FIXED 1

typedef struct uacpi_memory_attribute {
    uacpi_u8 write_status;
    uacpi_u8 caching;
    uacpi_u8 range_type;
    uacpi_u8 translation;
} uacpi_memory_attribute;

typedef struct uacpi_io_attribute {
    uacpi_u8 range_type;
    uacpi_u8 translation;
    uacpi_u8 translation_type;
} uacpi_io_attribute;

typedef union uacpi_address_attribute {
    uacpi_memory_attribute memory;
    uacpi_io_attribute io;
    uacpi_u8 type_specific;
} uacpi_address_attribute;

typedef struct uacpi_resource_address_common {
    uacpi_address_attribute attribute;
    uacpi_u8 type;
    uacpi_u8 direction;
    uacpi_u8 decode_type;
    uacpi_u8 fixed_min_address;
    uacpi_u8 fixed_max_address;
} uacpi_resource_address_common;

typedef struct uacpi_resource_address16 {
    uacpi_resource_address_common common;
    uacpi_u16 granularity;
    uacpi_u16 minimum;
    uacpi_u16 maximum;
    uacpi_u16 translation_offset;
    uacpi_u16 address_length;
    uacpi_resource_source source;
} uacpi_resource_address16;

typedef struct uacpi_resource_address32 {
    uacpi_resource_address_common common;
    uacpi_u32 granularity;
    uacpi_u32 minimum;
    uacpi_u32 maximum;
    uacpi_u32 translation_offset;
    uacpi_u32 address_length;
    uacpi_resource_source source;
} uacpi_resource_address32;

typedef struct uacpi_resource_address64 {
    uacpi_resource_address_common common;
    uacpi_u64 granularity;
    uacpi_u64 minimum;
    uacpi_u64 maximum;
    uacpi_u64 translation_offset;
    uacpi_u64 address_length;
    uacpi_resource_source source;
} uacpi_resource_address64;

typedef struct uacpi_resource_address64_extended {
    uacpi_resource_address_common common;
    uacpi_u8 revision_id;
    uacpi_u64 granularity;
    uacpi_u64 minimum;
    uacpi_u64 maximum;
    uacpi_u64 translation_offset;
    uacpi_u64 address_length;
    uacpi_u64 attributes;
} uacpi_resource_address64_extended;

typedef struct uacpi_resource_memory24 {
    uacpi_u8 write_status;
    uacpi_u16 minimum;
    uacpi_u16 maximum;
    uacpi_u16 alignment;
    uacpi_u16 length;
} uacpi_resource_memory24;

typedef struct uacpi_resource_memory32 {
    uacpi_u8 write_status;
    uacpi_u32 minimum;
    uacpi_u32 maximum;
    uacpi_u32 alignment;
    uacpi_u32 length;
} uacpi_resource_memory32;

typedef struct uacpi_resource_fixed_memory32 {
    uacpi_u8 write_status;
    uacpi_u32 address;
    uacpi_u32 length;
} uacpi_resource_fixed_memory32;

// compatibility & performance
#define UACPI_GOOD 0
#define UACPI_ACCEPTABLE 1
#define UACPI_SUB_OPTIMAL 2

typedef struct uacpi_resource_start_dependent {
    uacpi_u8 length_kind;
    uacpi_u8 compatibility;
    uacpi_u8 performance;
} uacpi_resource_start_dependent;

typedef struct uacpi_resource_vendor_defined {
    uacpi_u8 length;
    uacpi_u8 data[];
} uacpi_resource_vendor;

typedef struct uacpi_resource_vendor_typed {
    uacpi_u16 length;
    uacpi_u8 sub_type;
    uacpi_u8 uuid[16];
    uacpi_u8 data[];
} uacpi_resource_vendor_typed;

typedef struct uacpi_resource_generic_register {
    uacpi_u8 address_space_id;
    uacpi_u8 bit_width;
    uacpi_u8 bit_offset;
    uacpi_u8 access_size;
    uacpi_u64 address;
} uacpi_resource_generic_register;

// type
#define UACPI_GPIO_CONNECTION_INTERRUPT 0x00
#define UACPI_GPIO_CONNECTION_IO 0x01

typedef struct uacpi_interrupt_connection_flags {
    uacpi_u8 triggering;
    uacpi_u8 polarity;
    uacpi_u8 sharing;
    uacpi_u8 wake_capability;
} uacpi_interrupt_connection_flags;

// restriction
#define UACPI_IO_RESTRICTION_NONE 0x0
#define UACPI_IO_RESTRICTION_INPUT 0x1
#define UACPI_IO_RESTRICTION_OUTPUT 0x2
#define UACPI_IO_RESTRICTION_NONE_PRESERVE 0x3

typedef struct uacpi_io_connection_flags {
    uacpi_u8 restriction;
    uacpi_u8 sharing;
} uacpi_io_connection_flags;

// pull_configuration
#define UACPI_PIN_CONFIG_DEFAULT 0x00
#define UACPI_PIN_CONFIG_PULL_UP 0x01
#define UACPI_PIN_CONFIG_PULL_DOWN 0x02
#define UACPI_PIN_CONFIG_NO_PULL 0x03

typedef struct uacpi_resource_gpio_connection {
    uacpi_u8 revision_id;
    uacpi_u8 type;
    uacpi_u8 direction;

    union {
        uacpi_interrupt_connection_flags interrupt;
        uacpi_io_connection_flags io;
        uacpi_u16 type_specific;
    };

    uacpi_u8 pull_configuration;
    uacpi_u16 drive_strength;
    uacpi_u16 debounce_timeout;
    uacpi_u16 vendor_data_length;
    uacpi_u16 pin_table_length;
    uacpi_resource_source source;
    uacpi_u16 *pin_table;
    uacpi_u8 *vendor_data;
} uacpi_resource_gpio_connection;

// mode
#define UACPI_MODE_CONTROLLER_INITIATED 0x0
#define UACPI_MODE_DEVICE_INITIATED 0x1

typedef struct uacpi_resource_serial_bus_common {
    uacpi_u8 revision_id;
    uacpi_u8 type;
    uacpi_u8 mode;
    uacpi_u8 direction;
    uacpi_u8 sharing;
    uacpi_u8 type_revision_id;
    uacpi_u16 type_data_length;
    uacpi_u16 vendor_data_length;
    uacpi_resource_source source;
    uacpi_u8 *vendor_data;
} uacpi_resource_serial_bus_common;

// addressing_mode
#define UACPI_I2C_7BIT 0x0
#define UACPI_I2C_10BIT 0x1

typedef struct uacpi_resource_i2c_connection {
    uacpi_resource_serial_bus_common common;
    uacpi_u8 addressing_mode;
    uacpi_u16 slave_address;
    uacpi_u32 connection_speed;
} uacpi_resource_i2c_connection;

// wire_mode
#define UACPI_SPI_4_WIRES 0
#define UACPI_SPI_3_WIRES 1

// device_polarity
#define UACPI_SPI_ACTIVE_LOW 0
#define UACPI_SPI_ACTIVE_HIGH 1

// phase
#define UACPI_SPI_PHASE_FIRST 0
#define UACPI_SPI_PHASE_SECOND 0

// polarity
#define UACPI_SPI_START_LOW 0
#define UACPI_SPI_START_HIGH 1

typedef struct uacpi_resource_spi_connection {
    uacpi_resource_serial_bus_common common;
    uacpi_u8 wire_mode;
    uacpi_u8 device_polarity;
    uacpi_u8 data_bit_length;
    uacpi_u8 phase;
    uacpi_u8 polarity;
    uacpi_u16 device_selection;
    uacpi_u32 connection_speed;
} uacpi_resource_spi_connection;

// stop_bits
#define UACPI_UART_STOP_BITS_NONE 0b00
#define UACPI_UART_STOP_BITS_1 0b01
#define UACPI_UART_STOP_BITS_1_5 0b10
#define UACPI_UART_STOP_BITS_2 0b11

// data_bits
#define UACPI_UART_DATA_5BITS 0b000
#define UACPI_UART_DATA_6BITS 0b001
#define UACPI_UART_DATA_7BITS 0b010
#define UACPI_UART_DATA_8BITS 0b011
#define UACPI_UART_DATA_9BITS 0b100

// endianness
#define UACPI_UART_LITTLE_ENDIAN 0
#define UACPI_UART_BIG_ENDIAN 1

// parity
#define UACPI_UART_PARITY_NONE 0x00
#define UACPI_UART_PARITY_EVEN 0x01
#define UACPI_UART_PARITY_ODD 0x02
#define UACPI_UART_PARITY_MARK 0x03
#define UACPI_UART_PARITY_SPACE 0x04

// lines_enabled
#define UACPI_UART_DATA_CARRIER_DETECT (1 << 2)
#define UACPI_UART_RING_INDICATOR (1 << 3)
#define UACPI_UART_DATA_SET_READY (1 << 4)
#define UACPI_UART_DATA_TERMINAL_READY (1 << 5)
#define UACPI_UART_CLEAR_TO_SEND (1 << 6)
#define UACPI_UART_REQUEST_TO_SEND (1 << 7)

// flow_control
#define UACPI_UART_FLOW_CONTROL_NONE 0b00
#define UACPI_UART_FLOW_CONTROL_HW 0b01
#define UACPI_UART_FLOW_CONTROL_XON_XOFF 0b10

typedef struct uacpi_resource_uart_connection {
    uacpi_resource_serial_bus_common common;
    uacpi_u8 stop_bits;
    uacpi_u8 data_bits;
    uacpi_u8 endianness;
    uacpi_u8 parity;
    uacpi_u8 lines_enabled;
    uacpi_u8 flow_control;
    uacpi_u32 baud_rate;
    uacpi_u16 rx_fifo;
    uacpi_u16 tx_fifo;
} uacpi_resource_uart_connection;

// phy_type
#define UACPI_CSI2_PHY_C 0b00
#define UACPI_CSI2_PHY_D 0b01

typedef struct uacpi_resource_csi2_connection {
    uacpi_resource_serial_bus_common common;
    uacpi_u8 phy_type;
    uacpi_u8 local_port;
} uacpi_resource_csi2_connection;

typedef struct uacpi_resource_pin_function {
    uacpi_u8 revision_id;
    uacpi_u8 sharing;
    uacpi_u8 pull_configuration;
    uacpi_u16 function_number;
    uacpi_u16 pin_table_length;
    uacpi_u16 vendor_data_length;
    uacpi_resource_source source;
    uacpi_u16 *pin_table;
    uacpi_u8 *vendor_data;
} uacpi_resource_pin_function;

// type
#define UACPI_PIN_CONFIG_DEFAULT 0x00
#define UACPI_PIN_CONFIG_BIAS_PULL_UP 0x01
#define UACPI_PIN_CONFIG_BIAS_PULL_DOWN 0x02
#define UACPI_PIN_CONFIG_BIAS_DEFAULT 0x03
#define UACPI_PIN_CONFIG_BIAS_DISABLE 0x04
#define UACPI_PIN_CONFIG_BIAS_HIGH_IMPEDANCE 0x05
#define UACPI_PIN_CONFIG_BIAS_BUS_HOLD 0x06
#define UACPI_PIN_CONFIG_DRIVE_OPEN_DRAIN 0x07
#define UACPI_PIN_CONFIG_DRIVE_OPEN_SOURCE 0x08
#define UACPI_PIN_CONFIG_DRIVE_PUSH_PULL 0x09
#define UACPI_PIN_CONFIG_DRIVE_STRENGTH 0x0A
#define UACPI_PIN_CONFIG_SLEW_RATE 0x0B
#define UACPI_PIN_CONFIG_INPUT_DEBOUNCE 0x0C
#define UACPI_PIN_CONFIG_INPUT_SCHMITT_TRIGGER 0x0D

typedef struct uacpi_resource_pin_configuration {
    uacpi_u8 revision_id;
    uacpi_u8 sharing;
    uacpi_u8 direction;
    uacpi_u8 type;
    uacpi_u32 value;
    uacpi_u16 pin_table_length;
    uacpi_u16 vendor_data_length;
    uacpi_resource_source source;
    uacpi_u16 *pin_table;
    uacpi_u8 *vendor_data;
} uacpi_resource_pin_configuration;

typedef struct uacpi_resource_label {
    uacpi_u16 length;
    const uacpi_char *string;
} uacpi_resource_label;

typedef struct uacpi_resource_pin_group {
    uacpi_u8 revision_id;
    uacpi_u8 direction;
    uacpi_u16 pin_table_length;
    uacpi_u16 vendor_data_length;
    uacpi_resource_label label;
    uacpi_u16 *pin_table;
    uacpi_u8 *vendor_data;
} uacpi_resource_pin_group;

typedef struct uacpi_resource_pin_group_function {
    uacpi_u8 revision_id;
    uacpi_u8 sharing;
    uacpi_u8 direction;
    uacpi_u16 function;
    uacpi_u16 vendor_data_length;
    uacpi_resource_source source;
    uacpi_resource_label label;
    uacpi_u8 *vendor_data;
} uacpi_resource_pin_group_function;

typedef struct uacpi_resource_pin_group_configuration {
    uacpi_u8 revision_id;
    uacpi_u8 sharing;
    uacpi_u8 direction;
    uacpi_u8 type;
    uacpi_u32 value;
    uacpi_u16 vendor_data_length;
    uacpi_resource_source source;
    uacpi_resource_label label;
    uacpi_u8 *vendor_data;
} uacpi_resource_pin_group_configuration;

// scale
#define UACPI_SCALE_HZ 0b00
#define UACPI_SCALE_KHZ 0b01
#define UACPI_SCALE_MHZ 0b10

// frequency
#define UACPI_FREQUENCY_FIXED 0x0
#define UACPI_FREQUENCY_VARIABLE 0x1

typedef struct uacpi_resource_clock_input {
    uacpi_u8 revision_id;
    uacpi_u8 frequency;
    uacpi_u8 scale;
    uacpi_u16 divisor;
    uacpi_u32 numerator;
    uacpi_resource_source source;
} uacpi_resource_clock_input;

typedef struct uacpi_resource {
    uacpi_u32 type;
    uacpi_u32 length;

    union {
        uacpi_resource_irq irq;
        uacpi_resource_extended_irq extended_irq;
        uacpi_resource_dma dma;
        uacpi_resource_fixed_dma fixed_dma;
        uacpi_resource_io io;
        uacpi_resource_fixed_io fixed_io;
        uacpi_resource_address16 address16;
        uacpi_resource_address32 address32;
        uacpi_resource_address64 address64;
        uacpi_resource_address64_extended address64_extended;
        uacpi_resource_memory24 memory24;
        uacpi_resource_memory32 memory32;
        uacpi_resource_fixed_memory32 fixed_memory32;
        uacpi_resource_start_dependent start_dependent;
        uacpi_resource_vendor vendor;
        uacpi_resource_vendor_typed vendor_typed;
        uacpi_resource_generic_register generic_register;
        uacpi_resource_gpio_connection gpio_connection;
        uacpi_resource_serial_bus_common serial_bus_common;
        uacpi_resource_i2c_connection i2c_connection;
        uacpi_resource_spi_connection spi_connection;
        uacpi_resource_uart_connection uart_connection;
        uacpi_resource_csi2_connection csi2_connection;
        uacpi_resource_pin_function pin_function;
        uacpi_resource_pin_configuration pin_configuration;
        uacpi_resource_pin_group pin_group;
        uacpi_resource_pin_group_function pin_group_function;
        uacpi_resource_pin_group_configuration pin_group_configuration;
        uacpi_resource_clock_input clock_input;
    };
} uacpi_resource;

#define UACPI_NEXT_RESOURCE(cur) \
    ((uacpi_resource*)((uacpi_u8*)(cur) + (cur)->length))

typedef struct uacpi_resources {
    uacpi_size length;
    uacpi_resource *entries;
} uacpi_resources;
void uacpi_free_resources(uacpi_resources*);

typedef uacpi_iteration_decision (*uacpi_resource_iteration_callback)
    (void *user, uacpi_resource *resource);

uacpi_status uacpi_get_current_resources(
    uacpi_namespace_node *device, uacpi_resources **out_resources
);

uacpi_status uacpi_get_possible_resources(
    uacpi_namespace_node *device, uacpi_resources **out_resources
);

uacpi_status uacpi_set_resources(
    uacpi_namespace_node *device, uacpi_resources *resources
);

uacpi_status uacpi_for_each_resource(
    uacpi_resources *resources, uacpi_resource_iteration_callback cb, void *user
);

uacpi_status uacpi_for_each_device_resource(
    uacpi_namespace_node *device, const uacpi_char *method,
    uacpi_resource_iteration_callback cb, void *user
);

#ifdef __cplusplus
}
#endif
